﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;

namespace Gmantis.Controls
{
    public class BrushBuilder : FrameworkElement, INotifyPropertyChanged, ISupportInitialize
    {
        // Fields
        private Brush _brush = new SolidColorBrush(Colors.Transparent);
        public static readonly DependencyProperty DesignBrushProperty = DependencyProperty.Register("DesignBrush", typeof(Brush), typeof(BrushBuilder), new PropertyMetadata(null, new PropertyChangedCallback(BrushBuilder.OnDesignBrushPropertyChanged)));
        public static readonly DependencyProperty DesignColorProperty = DependencyProperty.Register("DesignColor", typeof(Color), typeof(BrushBuilder), new PropertyMetadata(new Color(), new PropertyChangedCallback(BrushBuilder.OnDesignColorPropertyChanged)));
        public static readonly DependencyProperty InputProperty = DependencyProperty.Register("Input", typeof(Brush), typeof(BrushBuilder), new PropertyMetadata(null, new PropertyChangedCallback(BrushBuilder.OnInputPropertyChanged)));

        // Events
        public event PropertyChangedEventHandler PropertyChanged;

        // Methods
        public BrushBuilder()
        {
            base.Visibility = System.Windows.Visibility.Collapsed;
            base.IsHitTestVisible = false;
            this.ExtrapolationMethod = BrushBuilderExtrapolationMethod.Absolute;
            base.Loaded += new RoutedEventHandler(this.BrushBuilderLoaded);
        }

        private void BrushBuilderLoaded(object sender, System.Windows.RoutedEventArgs e)
        {
            BindingExpression expression = base.ReadLocalValue(InputProperty) as BindingExpression;
            if (((expression != null) && (expression.ParentBinding.RelativeSource != null)) &&
                (expression.ParentBinding.RelativeSource.Mode == RelativeSourceMode.TemplatedParent))
            {
                base.ClearValue(InputProperty);
                base.SetBinding(InputProperty, expression.ParentBinding);
            }
        }

        private static Color GetColor(Brush brush)
        {
            return (Color)ColorConverter.Convert(brush, typeof(Color));
        }

        internal static Color GetRelativeColor(Color baseColor1, Color baseColor2, Color relativeColor)
        {
            HslColor color3;
            HslColor color = new HslColor(baseColor1);
            HslColor color2 = new HslColor(baseColor2);
            color3 = new HslColor(relativeColor);
            color3.Saturation = color3.Saturation + (color2.Saturation - color.Saturation);
            color3.Luminance = color3.Luminance + (color2.Luminance - color.Luminance);
            color3.Alpha = (byte)Math.Max(0, Math.Min(0xff, color3.Alpha + (color2.Alpha - color.Alpha)));

            return color3.ToColor();
        }

        private static void OnDesignBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as BrushBuilder).OnOutputChanged();
        }

        private static void OnDesignColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as BrushBuilder).OnOutputChanged();
        }

        private static void OnInputPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as BrushBuilder).OnOutputChanged();
        }

        private void OnOutputChanged()
        {
            if (this.IsInitialized)
            {
                Brush input = this.Input;
                Brush designBrush = this.DesignBrush;
                Color designColor = this.DesignColor;
                if (input != null)
                {
                    if (input is SolidColorBrush)
                    {
                        if (designBrush is SolidColorBrush)
                        {
                            SolidColorBrush brush3 = designBrush as SolidColorBrush;
                            SolidColorBrush brush4 = new SolidColorBrush(GetRelativeColor(designColor, brush3.Color, GetColor(input)));
                            brush4.Opacity = input.Opacity;

                            this._brush = brush4;
                        }
                        else if (designBrush is GradientBrush)
                        {
                            GradientBrush brush5;
                            GradientBrush brush6 = designBrush as GradientBrush;
                            if (designBrush is LinearGradientBrush)
                            {
                                LinearGradientBrush brush7 = designBrush as LinearGradientBrush;
                                LinearGradientBrush brush8 = new LinearGradientBrush
                                {
                                    StartPoint = brush7.StartPoint,
                                    EndPoint = brush7.EndPoint
                                };
                                brush5 = brush8;
                            }
                            else
                            {
                                RadialGradientBrush brush9 = designBrush as RadialGradientBrush;
                                RadialGradientBrush brush10 = new RadialGradientBrush
                                {
                                    Center = brush9.Center,
                                    GradientOrigin = brush9.GradientOrigin,
                                    RadiusX = brush9.RadiusX,
                                    RadiusY = brush9.RadiusY
                                };
                                brush5 = brush10;
                            }
                            brush5.ColorInterpolationMode = brush6.ColorInterpolationMode;
                            brush5.MappingMode = brush6.MappingMode;
                            brush5.RelativeTransform = brush6.RelativeTransform;
                            brush5.SpreadMethod = brush6.SpreadMethod;
                            brush5.Transform = brush6.Transform;
                            brush5.Opacity = input.Opacity;
                            Color color = GetColor(input);
                            foreach (GradientStop stop in brush6.GradientStops)
                            {
                                GradientStop stop2 = new GradientStop
                                {
                                    Offset = stop.Offset,
                                    Color = GetRelativeColor(designColor, stop.Color, color)
                                };
                                brush5.GradientStops.Add(stop2);
                            }
                            this._brush = brush5;
                        }
                        else
                        {
                            this._brush = designBrush;
                        }
                    }
                    else
                    {
                        this._brush = input;
                    }
                }
                else
                {
                    this._brush = designBrush;
                }

                if (this.PropertyChanged != null)
                    this.PropertyChanged(this, new PropertyChangedEventArgs("Output"));
            }
        }

        void ISupportInitialize.BeginInit()
        {
            this.IsInitialized = false;
        }

        void ISupportInitialize.EndInit()
        {
            this.IsInitialized = true;
            this.OnOutputChanged();
        }

        // Properties
        public Brush DesignBrush
        {
            get { return (Brush)base.GetValue(DesignBrushProperty); }
            set { base.SetValue(DesignBrushProperty, value); }
        }

        public Color DesignColor
        {
            get { return (Color)base.GetValue(DesignColorProperty); }
            set { base.SetValue(DesignColorProperty, value); }
        }

        public BrushBuilderExtrapolationMethod ExtrapolationMethod { get; set; }

        public Brush Input
        {
            get { return (Brush)base.GetValue(InputProperty); }
            set { base.SetValue(InputProperty, value); }
        }

        private bool IsInitialized { get; set; }

        public Brush Output
        {
            get { return this._brush; }
        }
    }

    public enum BrushBuilderExtrapolationMethod
    {
        Absolute
    }
}
